
#define SOL_ALL_SAFETIES_ON 1
#include <sol/sol.hpp>

#include "../luaParameters.h"
#include "../global.h"
#include <iostream>
#include <boost/filesystem.hpp>
#include <log/log.h>
#include <appendix/phy_constants.h>
#include <boost/signals2/signal.hpp>
#include <boost/bind.hpp>
#include <boost/regex.hpp>
#include <thread>
#include <boost/thread/mutex.hpp>
#include <iostream>
#include <tuple>

namespace LuaHelp{
	sol::table open_luaParameters(sol::this_state L) ;
}

namespace lua{
/*
	template <typename ...Args>
	inline bool safe_call_lua_function( sol::table module, const std::string& funName, Args&... args){
		sol::protected_function problematic_woof = module[funName];
		auto firstwoof = problematic_woof(module,args...);
		if ( firstwoof.valid() ) {
		}else
		{
			sol::error err = firstwoof;
			std::string what = err.what();
			logs::error_log("Call function %s:%s", funName.c_str(), what.c_str());
			return false;
		}
		return true;
	}
*/
	template <typename TT, typename ...Args>
	inline void safe_call_lua_function( sol::table module,
			                     const std::string& funName,
								 TT& results,
								 Args&... args){
		sol::protected_function problematic_woof = module[funName];
		auto firstwoof = problematic_woof(module,args...);
		if ( firstwoof.valid() ) {
		}else
		{
			sol::error err = firstwoof;
			std::string what = err.what();
			logs::error_log(boost::format("Call function %1%: %2%") % funName % what);
			return;
		}

		 sol::optional<TT> maybe_value = firstwoof;
		 if( maybe_value.has_value() ){
			 results = *maybe_value;
		  }else{
			  logs::error_log("Return type error");
			  return;
		  }
	}

	template <typename TT, typename ...Args>
	inline void call_lua_function( sol::table module,
			                     const std::string& funName,
								 TT& results,
								 Args&... args){
		results =  module[funName](module,args...);
	}

	luaParameters::luaParameters()
	:_state(std::make_shared<lua::luaState>()),
	 m_bNeedUpdate(true)
	{
		initState();
		parse();
	}

	luaParameters::luaParameters(const luaParameters& rhs)
	:_state(std::make_shared<lua::luaState>()),
	 m_bNeedUpdate(true){
		initState();
		add_variable( rhs.getList() );
		parse();
	}


	luaParameters::luaParameters(std::shared_ptr<luaState> state)
	:m_bNeedUpdate(true)
	{
		if(!state){
			logs::error_log("invaliate state");
		}
		_state = state;
		initState();
	}
	void luaParameters::setState(std::shared_ptr<luaState> state)
	{
		if(state){
			_state = state;
			initState();
			m_bNeedUpdate = true;
		}else{
			logs::error_log("invaluate state");
		}
	}

	bool luaParameters::initState()
	{
		if(_state){
			 std::scoped_lock lk( *( _state->mutex() ) );
			 sol::state_view lua( _state->state() );
			 lua.open_libraries(sol::lib::base,
									  sol::lib::package,
									  sol::lib::math,
									  sol::lib::string,
									  sol::lib::table,
									  sol::lib::io
									  );
			 m_module = LuaHelp::open_luaParameters( sol::this_state(_state->state()) );
			 return true;
		 }
		 logs::error_log("init state error");
		 return false;
	}


	bool luaParameters::parse()
	{
		if(_state){
			std::scoped_lock lk( *( _state->mutex() ) );
			sol::object res = m_module["parse"](m_module);
			if( res.get_type() == sol::type::boolean){
				return res.as<bool>();
			}
		}
		return false;
	}


	std::vector< std::vector<std::string> >  luaParameters::getList() const
	{
		std::vector< std::vector<std::string> >  res;
		if(_state){
			std::scoped_lock lk( *( _state->mutex() ) );
			//m_module["paser"](m_module);
			sol::protected_function problematic_woof = m_module["getList"];
			auto result = problematic_woof(m_module);
			if ( result.valid() ) {
				sol::table list = result;
				for (auto key_value_pair : list) {
					const sol::object& key = key_value_pair.first;
					const sol::object& value = key_value_pair.second;
					sol::type t = value.get_type();
					if( t == sol::type::table ){
						sol::table inner = value.as<sol::table>();
						std::vector<std::string> val(9);
						val[0] = inner["name"];
						val[1] = inner["expr"];
						val[2] = inner["value"];
						val[3] = inner["description"];
						val[4] = inner["msg"];
						val[5] = inner["values_str"];
						val[6] = inner["values"];
						val[7] = inner["sweep_state"];
						val[8] = inner["expr_impl"];
						res.push_back(val);
					}
				}
			}else{
				sol::error err = result;
				std::string what = err.what();
				logs::error_log(boost::format("call failed get list error: %1%") % what);
			}
		}
		return res;
	}

	bool luaParameters::setList(const std::vector< std::vector<std::string> >& list){
		return add_variable(list);
	}




	bool luaParameters::set_variable(const std::string& key , const std::string& val)
	{
		std::scoped_lock lk( *( _state->mutex() ) );
		m_module["change_variable_expression"](m_module, key, val);
		m_module["parse"](m_module);
		return true;
	}

	bool luaParameters::set_variable(const std::string& key , const std::string& val,const std::string& des)
	{
		std::scoped_lock lk( *( _state->mutex() ) );
		m_module["change_variable_expression"](m_module, key, val);
		m_module["parse"](m_module);

		sol::object res = m_module["change_variable_description"](m_module, key, des);
		if( res.get_type() == sol::type::boolean){
			return res.as<bool>();
		}

		return true;
	}

	bool luaParameters::add_variable(const std::string& key , const std::string& val)
	{
		std::scoped_lock lk( *( _state->mutex() ) );
		m_module["add_variable"](m_module, key, val,"None");
		m_module["parse"](m_module);
		return true;
	}

	bool luaParameters::add_variable(const std::string& key , const std::string& val,const std::string& des)
	{
		std::scoped_lock lk( *( _state->mutex() ) );
		m_module["add_variable"](m_module, key, val, des);
		m_module["parse"](m_module);
		return true;
	}

	bool luaParameters::add_variable(const std::string& key , const std::string& val,const std::string& des,const std::string& val_impl)
	{
		std::scoped_lock lk( *( _state->mutex() ) );
		m_module["add_variable"](m_module, key, val, des, val_impl);
		m_module["parse"](m_module);
		return true;
	}

	bool luaParameters::add_variable( const std::vector< std::vector<std::string> > & fields)
	{
		std::scoped_lock lk( *( _state->mutex() ) );
		for(auto&& p:fields)
		{
			//if(p.size()>8){
			//	m_module["add_variable"](m_module, p[0], p[1],p[3],p[8]);
			//}else{
				m_module["add_variable"](m_module, p[0], p[1],p[3]);
			//}
			m_module["change_variable_sweep_values"](m_module, p[0], p[5]);
			m_module["change_variable_sweep_state"](m_module, p[0], p[7]);
		}
		m_module["parse"](m_module);
		return true;
	}

	bool luaParameters::remove_all()
	{
		std::scoped_lock lk( *( _state->mutex() ) );
		sol::object res = m_module["remove_all"](m_module);
		if( res.get_type() == sol::type::number){
			return res.as<double>();
		}
		return false;
	}

	bool luaParameters::remove_variable(const std::string& key)
	{
		std::scoped_lock lk( *( _state->mutex() ) );
		sol::object res = m_module["remove_variable"](m_module, key);
		if( res.get_type() == sol::type::number){
			return res.as<double>();
		}
		return false;
	}

	bool luaParameters::evaluate(const std::string& expr,double& value)
	{
		if(!_state){
			logs::error_log(boost::format("evaluate expression %1%: state invaliate") % expr);
			return false;
		}
		std::scoped_lock lk( *( _state->mutex() ) );
		sol::protected_function problematic_woof = m_module["evaluate"];
		auto result = problematic_woof(m_module,expr);
		if ( result.valid() ) {
			std::tuple<bool,double> res = result;
			if( std::get<0>(res) ){
				value = std::get<1>(res);
				return true;
			}
		}else{
			sol::error err = result;
			std::string what = err.what();
			logs::error_log(boost::format("evaluate expression %1%: %2%") % expr % what);
		}
		return false;
	}

	bool luaParameters::evaluate(const std::string& expr,std::string& value)
	{
		std::scoped_lock lk( *( _state->mutex() ) );
		sol::protected_function problematic_woof = m_module["evaluate_expr"];

		auto result = problematic_woof(m_module,expr);
		if ( result.valid() ) {
			std::tuple<bool,std::string> res = result;
			if( std::get<0>(res) ){
				value = std::get<1>(res);
				return true;
			}
		}else{
			sol::error err = result;
			std::string what = err.what();
			logs::error_log(boost::format("evaluate expression with parameters: %1%") % what);
		}

		return false;
	}

	bool luaParameters::change_variable_expression(const std::string& key , const std::string& val)
	{
		std::scoped_lock lk( *( _state->mutex() ) );
		sol::object res = m_module["change_variable_expression"](m_module, key, val);
		if( res.get_type() == sol::type::boolean){
			return res.as<bool>();
		}
		return false;
	}

	bool luaParameters::change_variable_name(const std::string& key,const std::string& newKey )
	{
		std::scoped_lock lk( *( _state->mutex() ) );

		sol::protected_function problematic_woof = m_module["change_variable_name"];
		auto result = problematic_woof(m_module,key,newKey);
		if ( result.valid() ) {
			sol::object res = result;
			if( res.get_type() == sol::type::boolean){
				return res.as<bool>();
			}
		}else{
			sol::error err = result;
			std::string what = err.what();
			logs::error_log(boost::format("call failed get list error: %1%") % what);
		}
		return false;
	}

	bool luaParameters::change_variable_description(const std::string& key , const std::string& val)
	{
		std::scoped_lock lk( *( _state->mutex() ) );
		sol::object res = m_module["change_variable_description"](m_module, key, val);
		if( res.get_type() == sol::type::boolean){
			return res.as<bool>();
		}
		return false;
	}
	bool luaParameters::change_variable_sweep_values(const std::string& key , const std::string& val)
	{
		std::scoped_lock lk( *( _state->mutex() ) );
		bool res;
		safe_call_lua_function(m_module, "change_variable_sweep_values", res, key, val);
		return res;
		/*
		sol::object res = m_module["change_variable_sweep_values"](m_module, key, val);
		if( res.get_type() == sol::type::boolean){
			return res.as<bool>();
		}
		return false;
		*/
	}
	bool luaParameters::change_variable_sweep_state(const std::string& key , const std::string& val)
	{
		std::scoped_lock lk( *( _state->mutex() ) );

		bool res;
		safe_call_lua_function(m_module, "change_variable_sweep_state", res, key, val);
		/*
		sol::object res = m_module["change_variable_sweep_state"](m_module, key, val);
		if( res.get_type() == sol::type::boolean){
			return res.as<bool>();
		}
		*/
		return res;
	}

	bool luaParameters::change_variable_sweep_state(const std::string& key , bool val)
	{
		if(val)
			return change_variable_sweep_state(key,std::string("true"));
		return change_variable_sweep_state(key,std::string("false"));
	}

	bool luaParameters::get_variable_sweep_state(const std::string& key){
		std::scoped_lock lk( *( _state->mutex() ) );
		sol::object res = m_module["get_variable_sweep_state"](m_module, key);
		if( res.get_type() == sol::type::boolean){
			return res.as<bool>();
		}
		return false;
	}

	bool luaParameters::check_variable_name(const std::string& key)
	{
		std::scoped_lock lk( *( _state->mutex() ) );

		bool res;
		safe_call_lua_function(m_module, "check_variable_name", res, key);
		return res;
	}

	bool luaParameters::swap_variables_index(const std::string& key1,const std::string& key2){
		std::scoped_lock lk( *( _state->mutex() ) );
		bool res;
		safe_call_lua_function(m_module, "swap_variables_index",res, key1, key2);
		return res;
	}


	bool luaParameters::check_expression(const std::string& key)
	{
		std::scoped_lock lk( *( _state->mutex() ) );

		bool res;
		safe_call_lua_function(m_module, "check_expression", res, key);
		return res;
	}

	bool luaParameters::init_sweep_list()
	{
		std::scoped_lock lk( *( _state->mutex() ) );

		bool res;
		safe_call_lua_function(m_module, "init_sweep_list", res);
		return res;

		/*
		sol::object res = m_module["init_sweep_list"](m_module);
		if( res.get_type() == sol::type::boolean){
			return res.as<bool>();
		}
		return false;
		*/
	}

	bool luaParameters::sweep_list(std::vector<std::string>& names, std::vector< std::vector<double> >& values)
	{
		std::scoped_lock lk( *( _state->mutex() ) );

		bool res;
		safe_call_lua_function(m_module, "get_sweep_list", res, names, values);
		return res;

		/*
		std::scoped_lock lk( *( _state->mutex() ) );
		sol::object res = m_module["get_sweep_list"](m_module,names,values);
		if( res.get_type() == sol::type::boolean){
			return res.as<bool>();
		}
		return false;
		*/
	}

	bool luaParameters::sweep_index(std::vector< std::vector<int> >& values){
		std::scoped_lock lk( *( _state->mutex() ) );
		bool res;
		safe_call_lua_function(m_module, "get_sweep_index", res, values);
		return res;
	}


	bool luaParameters::update_sweep_list_to_variables(int idx)
	{
		std::scoped_lock lk( *( _state->mutex() ) );

		bool res;
		safe_call_lua_function(m_module, "update_sweep_list_to_variables", res,idx);
		return res;

		/*
		std::scoped_lock lk( *( _state->mutex() ) );
		sol::object res = m_module["update_sweep_list_to_variables"](m_module,idx);
		if( res.get_type() == sol::type::boolean){
			return res.as<bool>();
		}
		return false;
		*/
	}


};


